"""Contains the player"""

from pyre.core import types
from pyre.ui import widgit, element, aim
from ruin import character, item

class PlayerSkill(character.Skill):

    def __init__(self, bonus_func, messenger, name, value=0, train_amount=.01):
        character.Skill.__init__(self, value)

        pair = messenger, '{} skill'.format(name)
        self.inform = lambda: messenger.append_message('%p <increase>', pair)
        self.bonus_func = bonus_func
        self.train_amount = train_amount

    def _bonus(self):
        try:
            return self.bonus_func()
        except KeyError:
            return 0

    def train(self, amount=None):
        prev_full = int(self.full)
        self.full += self.train_amount if amount is None else amount
        increase = int(self.full) - prev_full
        if increase > 0:
            self.curr += increase
            if amount is None:
                self.inform()

    def __int__(self):
        return int(self.curr) + self._bonus()


class CombatRoll(object):

    def __init__(self, base_func, default=(0, 0)):
        self.base_func = base_func
        self.default = default

    def __getitem__(self, index):
        try:
            return self.base_func()[index]
        except KeyError:
            return self.default[index]

    def __str__(self):
        return '{}d{}'.format(self[0], self[1])


class Player(character.Character, item.Inventory):
    """Player controlled character"""

    def __init__(self, term):
        face = types.ColoredChar('@', sprite_id=0)
        character.Character.__init__(self, face, 'Player', 20)
        item.Inventory.__init__(self)

        melee_bonus = lambda: self.slots['weapon'].melee
        self.melee = PlayerSkill(melee_bonus, self, 'melee', 5)

        weapon_base = lambda: self.slots['weapon'].damage
        self.weapon = CombatRoll(weapon_base, (1, 1))

        evade_bonus = lambda: self.slots['body'].evade
        self.evade = PlayerSkill(evade_bonus, self, 'evade', 1)

        protect_base = lambda: self.slots['body'].protect
        self.armor = CombatRoll(protect_base, (10, 10))

        ranged_bonus = lambda: self.slots['weapon'].flags['ranged:bonus']
        self.ranged = PlayerSkill(ranged_bonus, self, 'ranged', 5)

        ranged_damage = lambda: self.slots['weapon'].flags['ranged:damage']
        self.ranged_damage = CombatRoll(ranged_damage)

        self.stealth = character.Skill(0)

        self.term = term
        self.camera = widgit.Camera(self)

    def act(self):
        self.stat_regen()

        key = self.term.get_key()
        if key == 'q':
            self.expire()
        elif key == '>':
            self.world.descend(self)
        elif key == '<':
            self.world.ascend(self)
        elif key == 'g':
            self.pickup()
        elif key == 'd':
            self.drop()
        elif key == 'i':
            self.show_pack()
        elif key == 'w':
            self.equip()
        elif key == 'r':
            self.unequip()
        elif key == 'e':
            self.show_slots()
        elif key == 'x':
            element.look(self.term, self.camera)
        elif key == 'f':
            self.perform_ranged()
        else:
            delta = types.Direction.key_to_dir(key)
            if delta is not None:
                self.move(delta)

        return 2

    def info(self):
        """
        Player.inf(): str
        Returns the information string to be used by a TextWidgit
        """

        if self.has_flag('weapon', 'ranged'):
            ranged = 'ranged: (+{}, {})'
            ranged = ranged.format(self.ranged, self.ranged_damage)
        else:
            ranged = ''

        lines = ['Player',
                 '',
                 'hp: {}/{}'.format(int(self.hp), self.hp.full),
                 '',
                 'weapon: (+{}, {})'.format(self.melee, self.weapon),
                 'armor: [+{}, {}]'.format(self.evade, self.armor),
                 ranged]

        return '\n'.join(lines)

    def __str__(self):
        return 'you'

    def perform_ranged(self):
        if not self.has_flag('weapon', 'ranged'):
            self.append_message('Invalid ranged weapon')
        else:
            pos = aim.beam(self.term, self.camera, character.Character)
            target = None if pos is None else self.world.get_actor_at(pos)
            if target:
                character.Character.perform_ranged(self, target)
            else:
                self.append_message('Invalid target selection')
